﻿//===================================================================================
// Copyright (c) Ryan Garrett.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
using System;
using System.Collections.Generic;

namespace Caffeine.Core.Validation
{
    delegate void AddErrorDel(ref IEnumerable<ValidationError> errors, Int32 errorCode, String errorMessage);
    
    internal class ValueTypeValidationRule<T> : ValidationRule<T>
    {
        #region State

        private readonly T _minValue;
        private readonly T _maxValue;
        private readonly Boolean _isNullableType;
        private readonly String _minErrorFormatString;
        private readonly String _maxErrorFormatString;

        #endregion

        #region Constructors

        internal ValueTypeValidationRule
        (
            Boolean nonDefaultValueRequired,
            Boolean ignoreCommitted, 
            T minValue, 
            T maxValue, 
            String valueRequiredMessage,
            String minErrorFormatString, 
            String maxErrorFormatString, 
            Boolean isNullableType
        )   : base(nonDefaultValueRequired, valueRequiredMessage, ignoreCommitted)
        {
            _minValue = minValue;
            _maxValue = maxValue;
            _isNullableType = isNullableType;
            _minErrorFormatString = String.IsNullOrEmpty(minErrorFormatString) ? "Must be greater than or equal to {0}" : minErrorFormatString;
            _maxErrorFormatString = String.IsNullOrEmpty(maxErrorFormatString) ? "Must be less than or equal to {0}" : maxErrorFormatString;
        }

        #endregion

        #region Public

        public T MinValue
        {
            get
            {
                return _minValue;
            }
        }

        public T MaxValue
        {
            get
            {
                return _maxValue;
            }
        }

        public Boolean IsNullableType
        {
            get
            {
                return _isNullableType;
            }
        }

        public String MinErrorFormatString
        {
            get
            {
                return _minErrorFormatString;
            }
        }

        public String MaxErrorFormatString
        {
            get
            {
                return _maxErrorFormatString;
            }
        }

        public static void CheckIsValid<S>
        (
            AddErrorDel addError,
            S value, 
            S minValue, 
            S maxValue, 
            String minErrorFormatString, 
            String maxErrorFormatString, 
            ref IEnumerable<ValidationError> errors, 
            ref Boolean isValid
        )
        {
            IComparable<S> comparable = value as IComparable<S>;

#if DEBUG
            if (comparable == null)
            {
                throw new ArgumentException("All value types implement IComparable. The ValidationHelper class should have forced a value type to be used for T", "value");
            }
#endif

            Boolean isMinValid = comparable.CompareTo(minValue) >= 0;

            if (!isMinValid)
            {
                addError(ref errors, 0, String.Format(minErrorFormatString, minValue));
            }

            Boolean isMaxValid = comparable.CompareTo(maxValue) < 1;

            if (!isMaxValid)
            {
                addError(ref errors, 0, String.Format(maxErrorFormatString, maxValue));
            }

            isValid = isMinValid && isMaxValid;
        }

        #endregion

        #region Protected

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
        protected override void OnValidate(ModelProperty<T> property, ref IEnumerable<ValidationError> errors, ref Boolean isValid)
        {
            CheckIsValid(property.Parent.AddError, property.Value, MinValue, MaxValue, MinErrorFormatString, MaxErrorFormatString, ref errors, ref isValid);
        }

        #endregion
    }
}
